#include "Common.fx"
#include "Sampling.fx"

//--------------------------------------
struct a2v {
	a2vApplyBasic basic;
};

struct v2f {
	v2fApplyBasic basic;
};

//--------------------------------------
sampler ProjLightMapSamp : register(s2);
sampler ShadowMapFullSamp : register(s3);
sampler ShadowMapReducedSamp : register(s4);

//--------------------------------------
v2f vsMain(a2v input) {
	
	v2f output;
	
	ApplyBasicVertexProcess(output.basic, input.basic);
	
	return output;
}

//--------------------------------------
f2fb psMain(v2f input) {

	f2fb output;
	
	input.basic.lsPos.xy /= input.basic.lsPos.w;
	input.basic.lsPos.xy = saturate(input.basic.lsPos.xy);
	
	float4 lightMap = tex2D(ProjLightMapSamp, input.basic.lsPos.xy);
	
	if(input.basic.lsPos.z < 0.01)
		lightMap = 0;
	
	float2 smTexcoordFull, smTexcoordReduced;
	ShadowMapBaseTexcoordEx(smTexcoordFull, input.basic.lsPos.xy, g_InvShadowMapSize.y);
	ShadowMapBaseTexcoordEx(smTexcoordReduced, input.basic.lsPos.xy, g_InvShadowMapSize.y * 4.0);
	
	float2 lerpWeightFull = frac(smTexcoordFull * g_ShadowMapSize);
	float2 lerpWeightReduced = frac(smTexcoordReduced * g_ShadowMapSize * 0.25);
	
	float4 smValsFull[4];
	float4 smValsReduced[4];
	
	float4 smResultsFull[4];
	float4 smResultsReduced[4];
	
	float occluderDepth = 1.0;
	
	for(int i=0; i<4; ++i)
	{
		float4 sample;
		
		for(int j=0; j<4; ++j)
		{	
			FetchOffsetedSampleEx( sample, ShadowMapFullSamp, smTexcoordFull, g_Uniform4x4[i * 4 + j], g_InvShadowMapSize.x );
			smValsFull[i][j] = sample.x;
			
			FetchOffsetedSampleEx( sample, ShadowMapReducedSamp, smTexcoordReduced, g_Uniform4x4[i * 4 + j], g_InvShadowMapSize.x * 4.0 );
			smValsReduced[i][j] = sample.x;
		}
		
		sample = min(smValsFull[i], smValsReduced[i]);
		occluderDepth = min( occluderDepth, min(sample.x, min(sample.y, min(sample.z, sample.w) ) ) );
		
		// compare 4 samples
		smResultsFull[i] = input.basic.lsPos.z < smValsFull[i];
		smResultsReduced[i] = input.basic.lsPos.z < smValsReduced[i];
	}
	
	float2 shadowTerm = 0;
	float2 temp;

	for(int i=0; i<3; ++i)
	{
		for(int j=0; j<3; ++j)
		{
			BilinearFilterSample(temp.x, smResultsFull[i][j], smResultsFull[i][j+1], smResultsFull[i+1][j], smResultsFull[i+1][j+1], lerpWeightFull);
			BilinearFilterSample(temp.y, smResultsReduced[i][j], smResultsReduced[i][j+1], smResultsReduced[i+1][j], smResultsReduced[i+1][j+1], lerpWeightReduced);
			
			shadowTerm += temp;
		}
	}
	
	shadowTerm *= 1.0 / 9.0;
	
	float dist2Occluder = saturate( saturate(input.basic.lsPos.z - occluderDepth) / occluderDepth);
	dist2Occluder = saturate(g_VarPCFDistFactor * dist2Occluder);
	
	lightMap *= lerp(shadowTerm.x, shadowTerm.y, dist2Occluder);
	
	ApplyBasicFragmentProcess(output.c0, input.basic, lightMap);
	
#ifdef DEBUG_VARPCF
	output.c0 = dist2Occluder;//occluderDepth;
#endif

	return output;
}

//--------------------------------------
technique Apply {

	pass {
	
		AlphaBlendEnable	= false;
		AlphaTestEnable		= false;
		
		VertexShader		= compile vs_3_0 vsMain();
		PixelShader			= compile ps_3_0 psMain();
	}
}

//--------------------------------------